import logging
import os
from typing import Callable, List, Any, Optional, Tuple

from PySide6.QtCore import QThread, Signal, QObject

from services.config_service import ConfigService, ConfigKey
from services.contract_info_service import ContractInfoService
from services.backtesting_service import BacktestingService
from services.backtest_result_service import BacktestResultService


logger = logging.getLogger(__name__)


class OptimizationWorkerThread(QThread):
    """参数优化工作线程（控制层）。"""

    progress_updated = Signal(int, int, str)  # current, total, message
    optimization_finished = Signal(list)      # results
    optimization_error = Signal(str)          # error_message

    def __init__(
        self,
        backtest_params: dict,
        optimization_setting: dict,
        workspace_path: str,
        use_ga: bool,
        max_workers: int,
        backtesting_service: BacktestingService,
    ) -> None:
        super().__init__()
        self.backtest_params = backtest_params
        self.optimization_setting = optimization_setting
        self.workspace_path = workspace_path
        self.use_ga = use_ga
        self.max_workers = max_workers
        self.backtesting_service = backtesting_service

    def run(self) -> None:
        try:
            results = self.backtesting_service.run_optimization(
                self.backtest_params,
                self.optimization_setting,
                self.workspace_path,
                self.use_ga,
                self.max_workers,
                self._progress_callback,
            )
            self.optimization_finished.emit(results)
        except Exception as e:
            logger.exception(e)
            self.optimization_error.emit(str(e))

    def _progress_callback(self, current: int, total: int, message: str) -> None:
        self.progress_updated.emit(current, total, message)


class SingleVarietyBacktestingController(QObject):
    """单品种回测控制器：协调服务、处理业务流程并与视图通信。"""

    def __init__(self) -> None:
        super().__init__()
        self.contract_info_service = ContractInfoService()
        self.backtesting_service = BacktestingService()
        self.config_service = ConfigService()
        self.backtest_result_service = BacktestResultService()

        self.optimization_thread: Optional[OptimizationWorkerThread] = None

    # ---------- 数据加载 ----------
    def load_local_codes(self) -> List[str]:
        """从数据库加载可用本地代码列表。"""
        try:
            df = self.contract_info_service.get_all_contracts_from_db()
            if not df.empty:
                return [f"{row['vnpy_underlying_symbol']}.{row['exchange']}" for _, row in df.iterrows()]
            return []
        except Exception as e:
            logger.exception(e)
            raise

    def fetch_multiplier(self, local_code: str) -> Optional[int]:
        """查询合约乘数。"""
        if not local_code or '.' not in local_code:
            return None
        try:
            symbol, exchange = local_code.split('.')
            return self.contract_info_service.get_contract_multiplier_from_db(symbol, exchange)
        except Exception as e:
            logger.exception(e)
            raise

    # ---------- 策略参数 ----------
    def get_strategy_default_parameters(
        self, strategy_file_path: str, strategy_name: str
    ) -> Optional[dict]:
        """进入工作区上下文，加载策略默认参数。"""
        workspace_path = self.config_service.get(ConfigKey.WORKSPACE_PATH)
        if not workspace_path:
            raise RuntimeError("未设置工作区路径")
        original_cwd = os.getcwd()
        try:
            os.chdir(workspace_path)
            return self.backtesting_service.get_strategy_default_parameters(
                strategy_file_path, strategy_name
            )
        finally:
            os.chdir(original_cwd)

    # ---------- 回测 ----------
    def run_backtest(
        self, params: dict, log_callback: Callable[[str], None]
    ) -> Tuple[bool, str, Any, Any, Any, list, list, list]:
        """执行回测，并尽可能返回完整结果。"""
        workspace_path = self.config_service.get(ConfigKey.WORKSPACE_PATH)
        if not workspace_path:
            raise RuntimeError("未设置工作区路径")

        return self.backtesting_service.run_backtest(params, workspace_path, log_callback)

    def save_backtest_result(self, params: dict, statistics: dict, results: Any) -> Optional[int]:
        """保存回测结果到数据库。"""
        try:
            return self.backtest_result_service.save_backtest_result(params, statistics, results)
        except Exception as e:
            logger.exception(e)
            raise

    # ---------- 优化 ----------
    def start_optimization(
        self,
        backtest_params: dict,
        optimization_setting: dict,
        use_ga: bool,
        max_workers: int,
        on_progress: Callable[[int, int, str], None],
        on_finished: Callable[[list], None],
        on_error: Callable[[str], None],
    ) -> None:
        """启动优化线程并连接回调。"""
        workspace_path = self.config_service.get(ConfigKey.WORKSPACE_PATH)
        if not workspace_path:
            raise RuntimeError("未设置工作区路径")

        thread = OptimizationWorkerThread(
            backtest_params,
            optimization_setting,
            workspace_path,
            use_ga,
            max_workers,
            self.backtesting_service,
        )

        thread.progress_updated.connect(on_progress)
        thread.optimization_finished.connect(on_finished)
        thread.optimization_error.connect(on_error)

        self.optimization_thread = thread
        thread.start()


